

/*
 * sender.c
 *
 *  Created on: 2013. 10. 16.
 *      Author: jphong
 */

#include "np.h"
#include "packet.h"

typedef enum {
    Wait_for_call,
    Wait_for_ACK,
    Closing
} State;

extern int DEBUG_ON;    // print debug messages if set
static unsigned int    ReTxTimeout = 100;        // default timeout in msec
static char *localhost = "192.168.156.142";            // default receiver hostname
static char *port = "5678";                        // default receiver port

//Packet buffers
static Packet     sndPacket, rcvPacket;
/*
 * rdt3.0 sender
 */
int main( int argc, char **argv )
{
    printf ("Start sender\n");
    struct sockaddr_in peer ,from_adr;
    int     fd, s;
    unsigned char sndSeq;
    Event     event;
    State     state;
    Packet    *sndpkt = &sndPacket;
    Packet *rcvpkt = &rcvPacket;
    char    *data = &(sndPacket.data[0]);
    char    *hostname, *service;
    int     opt;

    socklen_t     adr_sz;

    // Command line option processing
    while ( (opt = getopt(argc, argv, "dt:")) != -1) {
        switch (opt) {
        case 'd':
            DEBUG_ON = 1;     // Debug mode
            break;
        case 't':
            ReTxTimeout = atoi(optarg);
            break;
        default:
            error(1, 0, "Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n", argv[0] );
        }
    }
    if (optind == argc-2) {
        hostname = argv[optind];
        service = argv[optind+1];
    } else if (optind == argc) {
        hostname = localhost;
        service = port;
    } else
        error(1, 0, "Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n", argv[0] );
    debug("To %s:%s\tTimeout = %dmsec\tDebugMode = %s\n", hostname, service, ReTxTimeout,
            DEBUG_ON ? "on" : "off");

    set_address( hostname, service, &peer, "udp" ); // Set peer's IP and port

    if ( (s = socket( AF_INET, SOCK_DGRAM, 0 )) < 0)
        error(1, errno, "socket creation failed\n" );

    fd = fileno(stdin);

    // Init conditiion
    sndSeq = 0;
    state = Wait_for_call;

    // FSM
    for (;;) {
        int datalen, rcvlen;

        event = getEvent(fd, s);
        // Actions taken by State and Event
        switch (state) {
        // Wait_for_call state
        case Wait_for_call:
            switch (event) {
            case RDT_SEND:
                if ( fgets( data, MAX_DATA, stdin) != NULL ) {  // Get data from above
                    datalen = strlen(sndPacket.data);
                    sndpkt = make_pkt(DATA, sndSeq, sndpkt, datalen);
                    rcvlen = udt_send (s, sndpkt, (struct sockaddr *)&peer, sizeof(peer));

                    state = Wait_for_ACK;
                    start_timer(ReTxTimeout);                            // change msec


                    /*
                     * More codes here ...
                     */
                    continue;
                } else {                // EoF encountered. Close the session
                    sndpkt = make_pkt(END, sndSeq, sndpkt, 0);
                    rcvlen = udt_send (s, sndpkt, (struct sockaddr *)&peer, sizeof(peer));
                    state = Closing;
                    start_timer(ReTxTimeout);                            // change msec



                    /*
                     * More codes here ...
                     */
                }
            default:
                continue;                // ignore the event
            }

        //Wait_for_ACK state
        case Wait_for_ACK:
            switch (event) {
            case RDT_RCV:
                rcvlen = recvfrom (s, rcvpkt, sizeof(Packet), 0, (struct sockaddr *)&from_adr, &adr_sz);

                if (rcvlen < 0)
                    error (1, errno, "recvfrom failed\n");

                if (isType(rcvpkt, ACK, sndSeq) && !isCorrupt(rcvpkt)) {

                    print_pkt ("RECV", rcvpkt);

                    sndSeq = (sndSeq + 1) % 2;
                    state = Wait_for_call;
                    stop_timer();

                }

                /*
                 * More codes here ...
                 */
                    continue;
            case TIMEOUT:

                rcvlen = udt_send (s, sndpkt, (struct sockaddr *)&peer, sizeof(peer));
                start_timer(ReTxTimeout);

                //state = Wait_for_call;

                /*
                 * More codes here ...
                 */
                continue;
            default:
                continue;
            }
        // Closing state
            // wait an ACK. Sender shall stop when anything hear from receiver
        case Closing:
            switch (event) {
            case RDT_RCV:
                rcvlen = recvfrom (s, rcvpkt, sizeof(Packet), 0, (struct sockaddr *)&from_adr, &adr_sz);

                if (rcvlen < 0)
                    error (1, errno, "recvfrom failed\n");

                if (isType(rcvpkt, END, sndSeq+1) && !isCorrupt(rcvpkt)) {
                    stop_timer();
                    printf ("END\n");
                    exit(0);
                }
                /*
                 * More codes here ...
                 */
            case TIMEOUT:

                rcvlen = udt_send (s, sndpkt, (struct sockaddr *)&peer, sizeof(peer));
                start_timer(ReTxTimeout);
                /*
                 * More codes here ...
                 */
                //continue;
            default:
                continue;
            }    // End of inner switch
        }    // End of outer switch

    }   // End of for loop

}    // End of main

